#include "crossbit/GLOBAL.h"
#include "crossbit/VBlock.h"
#include "crossbit/VInst.h"

#include "set"
using namespace std;
#define EXIT_BLOCKS (1000)
namespace crossbit {
    XTUint32 GLOBAL::last_exit = 0;
    XTUint32 GLOBAL::next_address = 0;
    XTUint32 GLOBAL::saved_esp = 0;
	XTUint32 GLOBAL::return_count = 0;
	XTUint32 GLOBAL::switch_addr = 0;
	XTMemAddr GLOBAL::exit_stub_base = 0;
	XTMemAddr GLOBAL::exit_stub_end = 0;
	XTMemAddr GLOBAL::super_stub_base = 0;
	XTMemAddr GLOBAL::super_stub_end = 0;
	XTMemAddr GLOBAL::hashtable_dispatch = 0;
	//XTMemAddr GLOBAL::return_addr_stack[1000] = {0}; //FIXME: obsolete ?
	XTMemAddr GLOBAL::indirect_hashtable[HASH_TABLE_SIZE] = {0};
    volatile XTUint32 GLOBAL::return_address = 0;

	XTUint32 GLOBAL::sig_ret_addr = 0;
    XTUint32 GLOBAL::SIG_RET = 0;
	sigset_t GLOBAL::SIG_FULL_SET = {0};
	sigset_t GLOBAL::SIG_ON_SET = {0};
	sigset_t GLOBAL::SIG_ON_OLDSET = {0};
	XTInt32 GLOBAL::SIG_HOW = SIG_SETMASK;

    TBlock* GLOBAL::tblock_stub = NULL;
    VBlock* GLOBAL::vblock_stub = NULL;
    XTUint32 GLOBAL::BUILD_SUPER_BLOCK = 0;
    XTUint32 GLOBAL::total_exit = 0;
	std::stack<std::pair<XTMemAddr, XTMemAddr> > GLOBAL::return_stack; 
    std::map<XTMemAddr ,std::vector<XTMemAddr> *> GLOBAL::return_map;
    XTUint32 GLOBAL::exit_stub[1000] = {0};
    Exit* GLOBAL::requestExit() {
                 int t = GLOBAL::total_exit/EXIT_BLOCKS;
                 if( total_exit % EXIT_BLOCKS == 0 ) {
                     GLOBAL::exit_stub[t] = (XTMemAddr)malloc(sizeof(Exit) * EXIT_BLOCKS );
                 }
                 Exit* p = (Exit*)(exit_stub[t] + sizeof(Exit) * (total_exit % EXIT_BLOCKS));
				 p -> exit_number = GLOBAL::total_exit;
                 total_exit++;
				 return p;
           }
   Exit* GLOBAL::getExit( XTUint32 exit_number ) {
                  if( exit_number >= GLOBAL::total_exit ) {
                      printf("ERROR in GLOBAL.cpp#getExit()\n");
					  exit(0);
				  }
                  int t = exit_number/EXIT_BLOCKS;
                  return (Exit *)(GLOBAL::exit_stub[t] + sizeof(Exit) * (exit_number % EXIT_BLOCKS));
           }

#define CHECK_MOD(a) dep_set.find((*it)-> getOperand(a).reg) != dep_set.end()
#define CHECK_DEL(a) dep_set.erase((*it) -> getOperand(a).reg)
#define CHECK_ADD(a) dep_set.insert((*it) -> getOperand(a).reg)

/**
 * if the address of the indirect jump is only depended on %gp(v28) , then this indirect jump is identifed as a function call
 */ 
   bool GLOBAL::checkCall( VBlock *vblock ) {
		VBlock::vb_reverse_iterator it = vblock -> rbegin();
		if( (*it) -> getOpcode() != OP_JMP ) {
			return false;
		}
		set<XTRegNum> dep_set;
		dep_set.insert((*it) -> getOperand(1).reg );
		while( (++it) != vblock -> rend() ) {
			if( *it == NULL ) {
				continue;
			}
			Opcode opcode = (*it) -> getOpcode();
			XTInt32 del , add;
			del = 0 , add = 0;
			switch ( opcode ) {
				case OP_GET :    if( CHECK_MOD(2) ){
										return false;
								 }
								 break;
				case OP_LD :     if( CHECK_MOD(4) ) {
										CHECK_DEL( 4 );
										CHECK_ADD( 1 );
								 }
								 break;
				case OP_MOV:     if( CHECK_MOD( 2 ) ) {
										CHECK_DEL( 2 );
										CHECK_ADD( 1 );
								 }
								 break;
				case OP_LI : 	 if( CHECK_MOD(2) ) {
										CHECK_DEL(2);
								 }
								 break;
				case OP_SUB :
				case OP_ADD :    if( CHECK_MOD( 3 ) ) {
										CHECK_DEL( 3 );
										CHECK_ADD( 1 ); 
										CHECK_ADD( 2 ); 
								 }
								 break;
				case OP_ADDI :    if( CHECK_MOD( 3 ) ) {
										CHECK_DEL( 3 );
										CHECK_ADD( 2 ); 
								 }
								 break;
				case OP_NOT  : if( CHECK_MOD( 2 ) ) {
									return false;
								}
								break;
				case OP_AND  :
				case OP_XOR  :
				case OP_OR  :
				case OP_SLL :
				case OP_SRL :
				case OP_SRA :
				case OP_CMP :
			    case OP_SEXT :
				case OP_ZEXT : 	 if( CHECK_MOD ( 3 ) ) {
										return false;
								 }
								 break;
									 
			    case OP_MUL :
				case OP_MULU : 
				case OP_DIV  :
				case OP_DIVU : 	  if( CHECK_MOD( 3 ) || CHECK_MOD( 4 ) ) {
										 return false;
								  }
			    					  break;
				default :		
							break;
			}
						
			if( dep_set.size() == 0 || dep_set.size() == 1 && dep_set.find( (XTRegNum)28) != dep_set.end() ) {
				return true;
			}
		}
		return false;
	}
	
}
